package com.by.toolkit.utils;


import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.by.toolkit.enums.ErrorCodeEnum;
import com.by.toolkit.exceptions.ServiceException;
import jakarta.validation.constraints.NotNull;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.SocketTimeoutException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;

/**
 * @author Tian Wei
 * @version 1.0
 * @date 2022/3/14 下午5:32
 */
public class ExceptionUtil {

    public static void trueError(boolean expression, @NotNull String errorMessage) {
        if (expression) {
            throw new ServiceException(errorMessage);
        }
    }

    public static void trueError(boolean expression, @NotNull String errorMessageTemplate, Object... objs) {
        if (expression) {
            throw new ServiceException(StrUtil.format(errorMessageTemplate, objs));
        }
    }

    public static void trueError(boolean expression, ErrorCodeEnum errorCodeEnum) {
        if (expression) {
            throw new ServiceException(errorCodeEnum);
        }
    }

    public static void falseError(boolean expression, @NotNull String errorMessage) {
        if (!expression) {
            throw new ServiceException(errorMessage);
        }
    }

    public static void falseError(boolean expression, ErrorCodeEnum errorCodeEnum) {
        if (!expression) {
            throw new ServiceException(errorCodeEnum);
        }
    }

    public static void falseError(boolean expression, @NotNull String errorMessageTemplate, Object... objs) {
        if (!expression) {
            throw new ServiceException(StrUtil.format(errorMessageTemplate, objs));
        }
    }

    public static void nullError(Object argument, @NotNull String errorMessage) {
        if (Objects.isNull(argument)) {
            throw new ServiceException(errorMessage);
        }

        if (argument instanceof String) {
            if (StringUtils.isBlank(argument.toString())) {
                throw new ServiceException(errorMessage);
            }
        } else if (argument instanceof Collection) {
            if (CollectionUtils.isEmpty((Collection<?>) argument)) {
                throw new ServiceException(errorMessage);
            }
        } else if (argument instanceof Map) {
            if (MapUtil.isEmpty((Map<?, ?>) argument)) {
                throw new ServiceException(errorMessage);
            }
        }
    }

    public static void nullError(Object argument, @NotNull ErrorCodeEnum errorCodeEnum) {
        if (Objects.isNull(argument)) {
            throw new ServiceException(errorCodeEnum);
        }

        if (argument instanceof String) {
            if (StringUtils.isBlank(argument.toString())) {
                throw new ServiceException(errorCodeEnum);
            }
        } else if (argument instanceof Collection) {
            if (CollectionUtils.isEmpty((Collection<?>) argument)) {
                throw new ServiceException(errorCodeEnum);
            }
        } else if (argument instanceof Map) {
            if (MapUtil.isEmpty((Map<?, ?>) argument)) {
                throw new ServiceException(errorCodeEnum);
            }
        }
    }

    public static void nullError(Object argument, @NotNull String errorMessageTemplate, Object... objs) {
        nullError(argument, StrUtil.format(errorMessageTemplate, objs));
    }

    public static void throwError(@NotNull String errorMessageTemplate, Object... objs) {
        throw new ServiceException(StrUtil.format(errorMessageTemplate, objs));
    }

    public static void throwError(ErrorCodeEnum errorCodeEnum) {
        throw new ServiceException(errorCodeEnum);
    }

    public static void throwUqIdxError() {
        throw new ServiceException(ErrorCodeEnum.DB_UK_IDX);
    }

    public static void throwUqIdxError(@NotNull String errorMessageTemplate, Object... objs) {
        throw new ServiceException(ErrorCodeEnum.DB_UK_IDX.getErrorCode(), StrUtil.format(errorMessageTemplate, objs));
    }

    public static void throwRedisLockError() {
        throw new ServiceException(ErrorCodeEnum.REDIS_LOCK_FAILED);
    }

    public static void notEmptyError(Object argument, @NotNull String errorMessageTemplate, Object... objs) {
        trueError(ObjectUtil.isNotEmpty(argument), StrUtil.format(errorMessageTemplate, objs));
    }


    public static String getStackTrace(final Throwable throwable) {

        if (throwable instanceof ServiceException) {
            ServiceException e = (ServiceException) throwable;
            return e.getMsg();
        }

        final StringWriter sw = new StringWriter();
        final PrintWriter pw = new PrintWriter(sw, true);
        throwable.printStackTrace(pw);
        return sw.getBuffer().toString();
    }

    public static boolean uqIndexError(final Throwable throwable) {
        Throwable cause = throwable.getCause();
        if (Objects.isNull(cause)) {
            return false;
        }
        Class<? extends Throwable> aClass = cause.getClass();
        if (Objects.isNull(aClass)) {
            return false;
        }
        return Objects.equals(SQLIntegrityConstraintViolationException.class.getName(), aClass.getName()) || throwable.getMessage().contains("Duplicate entry");
    }

    public static void uqIndexError(final Throwable throwable, @NotNull String errorMessageTemplate, Object... objs) {
        uqIndexError(throwable, StrUtil.format(errorMessageTemplate, objs));
    }

    public static void uqIndexError(final Throwable throwable, @NotNull String errorMessage) {
        if (!uqIndexError(throwable)) {
            return;
        }
        throw new ServiceException(errorMessage);
    }

    public static boolean httpTimeOutError(final Throwable throwable) {
        Throwable cause = throwable.getCause();
        if (Objects.isNull(cause)) {
            return false;
        }
        Class<? extends Throwable> aClass = cause.getClass();
        if (Objects.isNull(aClass)) {
            return false;
        }
        return Objects.equals(SocketTimeoutException.class.getName(), aClass.getName());
    }

}
